using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Org.Loon.Framework.Xna.Game.Core.Graphics;
using Org.Loon.Framework.Xna.Game.Core;
using Org.Loon.Framework.Xna.Game.Utils;
using System.IO;
using Org.Loon.Framework.Xna.Game.Action.Map;
using Org.Loon.Framework.Xna.Game.Utils.Xml;
using Org.Loon.framework.Xna.Java.Collections;
using Org.Loon.Framework.Xna.Game.Core.Graphics.OpenGL;
using Org.Loon.Framework.Xna.Game.Core.Geom;
using Org.Loon.Framework.Xna.Game.Core.Input;

namespace Org.Loon.Framework.Xna.Game.Action.Scripting.Pack
{
    public class PackTileMap : LLight, LRelease
    {

        internal class SimplePackTileFactory : PackTileFactory
        {

            Dictionary<Int32, PackTile> tiles = new Dictionary<Int32, PackTile>(10);

            public void Add(int id, PackTile tile)
            {
                tiles.Add(id, tile);
            }

            public override PackTile GetTile(int id)
            {
                return (PackTile)CollectionUtils.Get(tiles, id);
            }

            public bool Contains(int id)
            {
                return tiles.ContainsKey(id);
            }

            public override string GetName()
            {
                return "simple";
            }

        }

        private string bindPackName;

        private int posX, posY;

        private int sx, sy;

        private PackView view;

        private PackTileFactory tileFactory;

        private int[] limit;

        private bool init, dirty;

        private int tileWidth = 32;

        private int tileHeight = 32;

        private int width, height;

        private string name;

        private PackTile[][] tiles;

        private Field2D field;

        private XMLElement element;

        private Screen screen;

        private int limitWidth, limitHeight;

        private int minX, minY, maxX, maxY;

        public PackTileMap(string res)
            : this(res, null)
        {

        }

        public PackTileMap(Stream ins)
            : this(ins, null)
        {

        }

        public PackTileMap(XMLElement element)
            : this(element, null)
        {

        }

        public PackTileMap(PackTileFactory factory, XMLElement element)
            : this(factory, element, null)
        {

        }

        public PackTileMap(string res, Screen screen)
            : this(XMLParser.Parse(res).GetRoot(), screen)
        {

        }

        public PackTileMap(Stream ins, Screen screen)
            : this(XMLParser.Parse(ins).GetRoot(), screen)
        {

        }

        public PackTileMap(XMLElement element, Screen screen)
            : this(null, element, screen)
        {

        }

        public PackTileMap(PackTileFactory factory, XMLElement element,
                Screen screen)
        {
            this.screen = screen;
            this.tileFactory = factory;
            this.element = element;
            this.dirty = true;
        }

        internal class _Tile : PackTile
        {

            int id;
            int imgId;
            float sx;
            float sy;
            float sw;
            float sh;
            bool sub;
            string imgName;
            int tileWidth, tileHeight;
            bool solid;

            public _Tile(int id, int imgId, float sx, float sy, float sw, float sh, bool sub, string name, int w, int h)
            {
                this.id = id;
                this.imgId = imgId;
                this.sx = sx;
                this.sy = sy;
                this.sw = sw;
                this.sh = sh;
                this.sub = sub;
                this.imgName = name;
                this.tileWidth = w;
                this.tileHeight = h;
            }


            public override void Draw(LTexturePack pack, float x, float y,
                    LColor c)
            {
                if (c == null)
                {
                    if (imgId != -1)
                    {
                        if (sub)
                        {
                            pack.Draw(imgId, x, y, tileWidth,
                                    tileHeight, sx, sy, sw, sh);
                        }
                        else
                        {
                            pack.Draw(imgId, x, y);
                        }
                    }
                    else
                    {
                        if (sub)
                        {
                            pack.Draw(imgName, x, y, tileWidth,
                                    tileHeight, sx, sy, sw, sh);
                        }
                        else
                        {
                            pack.Draw(imgName, x, y);
                        }
                    }
                }
                else
                {
                    if (imgId != -1)
                    {
                        if (sub)
                        {
                            pack.DrawOnlyBatch(imgId, x, y,
                                    tileWidth, tileHeight, sx, sy,
                                    sw, sh, c);
                        }
                        else
                        {
                            pack.DrawOnlyBatch(imgId, x, y, c);
                        }
                    }
                    else
                    {
                        if (sub)
                        {
                            pack.DrawOnlyBatch(imgName, x, y,
                                    tileWidth, tileHeight, sx, sy,
                                    sw, sh, c);
                        }
                        else
                        {
                            pack.DrawOnlyBatch(imgName, x, y, c);
                        }
                    }
                }
            }


            public override bool IsSolid()
            {
                return solid;
            }

            public override void SetSolid(bool s)
            {
                this.solid = s;
            }

            public override int Width()
            {
                return tileWidth;
            }

            public override int Height()
            {
                return tileHeight;
            }

            public override void Update(long t)
            {

            }
        }



        public void Init(PackTileFactory tileFactory)
        {
            if (init)
            {
                return;
            }

            this.name = element.GetAttribute("name", null);

            this.bindPackName = element.GetAttribute("bind", null);

            this.tileWidth = element.GetIntAttribute("tw", tileWidth);
            if (tileWidth < 32)
            {
                tileWidth = 32;
            }

            this.tileHeight = element.GetIntAttribute("th", tileHeight);
            if (tileHeight < 32)
            {
                tileHeight = 32;
            }
            string limits = element.GetAttribute("limit", "");

            string[] list = StringUtils.Split(limits, ",");
            if (list != null && list.Length > 0)
            {
                int size = list.Length;
                limit = new int[size];
                for (int i = 0; i < size; i++)
                {
                    try
                    {
                        limit[i] = int.Parse(list[i]);
                    }
                    catch (Exception)
                    {
                        limit[i] = -1;
                    }
                }
            }

            if (tileFactory == null)
            {

                SimplePackTileFactory packTile = new SimplePackTileFactory();

                for (IIterator it = element.Elements("tile"); it.HasNext(); )
                {
                    XMLElement child = (XMLElement)it.Next();
                    int id = child.GetIntAttribute("id", -1);
                    int imgId = child.GetIntAttribute("blockid", -1);
                    float sx = child.GetIntAttribute("x", 0);
                    float sy = child.GetIntAttribute("y", 0);
                    float sw = child.GetIntAttribute("w", 0);
                    float sh = child.GetIntAttribute("h", 0);
                    bool sub = (sx != 0 || sy != 0 || sw != 0 || sh != 0);
                    string imgName = child.GetAttribute("blockname", "");

                    if (!packTile.Contains(id))
                    {

                        PackTile tile = new _Tile(id, imgId, sx, sy, sw, sh, sub, imgName, tileWidth, tileHeight);


                        packTile.Add(id, tile);
                    }
                }

                this.tileFactory = packTile;
            }
            List<int[]> records = new List<int[]>(10);
            for (IIterator e = element.Elements("b"); e.HasNext(); )
            {
                string result = ((XMLElement)e.Next()).GetAttribute("d", "");
                if (!"".Equals(result))
                {
                    string[] stringArray = StringUtils.Split(result, ",");
                    int size = stringArray.Length;
                    int[] intArray = new int[size];
                    for (int i = 0; i < size; i++)
                    {
                        intArray[i] = int.Parse(stringArray[i]);
                    }
                    records.Add(intArray);
                }
            }
            if (records.Count > 0)
            {
                int col = records.Count;
                int[][] result = new int[col][];
                for (int i = 0; i < col; i++)
                {
                    result[i] = (int[])records[i];
                }
                this.width = result[0].Length;
                this.height = result.Length;
                this.field = new Field2D(result, tileWidth, tileHeight);
            }

            if (screen != null)
            {
                limitWidth = screen.GetWidth() / tileWidth;
                limitHeight = screen.GetHeight() / tileHeight;
            }
            else
            {
                limitWidth = width;
                limitHeight = height;
            }

            this.MaxLightSize(width, height);
            this.maxX = limitWidth;
            this.maxY = limitHeight;
            this.init = true;
        }

        public void Sub(int x, int y, int w, int h)
        {
            this.minX = x;
            this.minY = y;
            this.maxX = w;
            this.maxY = h;
        }

        public void Sub(int x, int y)
        {
            Sub(x, y, limitWidth, limitHeight);
        }

        public bool IsDirty()
        {
            return dirty;
        }

        public void Update()
        {
            if (!dirty)
            {
                return;
            }

            if (field != null)
            {
                int[][] maps = field.GetMap();
                if (tiles == null)
                {
                    this.tiles = (PackTile[][])CollectionUtils.XNA_CreateJaggedArray(typeof(PackTile), this.width, this.height);
                }
                if (limit == null)
                {
                    for (int x = 0; x < width; x++)
                    {
                        for (int y = 0; y < height; y++)
                        {
                            tiles[x][y] = tileFactory.GetTile(maps[y][x]);
                            if (tiles[x][y] != null && maps[y][x] == -1)
                            {
                                tiles[x][y].SetSolid(true);
                            }
                        }
                    }
                }
                else
                {
                    field.SetLimit(limit);
                    for (int x = 0; x < width; x++)
                    {
                        for (int y = 0; y < height; y++)
                        {
                            int id = maps[y][x];
                            if (tiles[x][y] == null)
                            {
                                tiles[x][y] = tileFactory.GetTile(id);
                            }
                            if (tiles[x][y] != null)
                            {
                                if (limit != null)
                                {
                                    for (int i = 0; i < limit.Length; i++)
                                    {
                                        if (id == limit[i])
                                        {
                                            tiles[x][y].SetSolid(true);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            dirty = false;
        }

        public List<Vector2f> FindAStar(AStarFindHeuristic heuristic, int x1,
                int y1, LTouch touch)
        {
            if (field != null)
            {
                return FindAStar(heuristic, x1, y1, touch.X(), touch.Y());
            }
            return null;
        }

        public List<Vector2f> FindAStar(AStarFindHeuristic heuristic, int x1,
                int y1, int x2, int y2)
        {
            if (field != null)
            {
                return FindAStar(heuristic, x1, y1, x2, y2, true);
            }
            return null;
        }

        public List<Vector2f> FindAStar(AStarFindHeuristic heuristic, int x1,
                int y1, bool flag, LTouch touch)
        {
            if (field != null)
            {
                return FindAStar(heuristic, x1, y1, touch.X(), touch.Y(), flag);
            }
            return null;
        }

        public List<Vector2f> FindAStar(AStarFindHeuristic heuristic, int x1,
                int y1, int x2, int y2, bool flag)
        {
            if (field != null)
            {
                return AStarFinder.Find(heuristic, field,
                        field.PixelsToTilesWidth(x1),
                        field.PixelsToTilesHeight(y1),
                        field.PixelsToTilesWidth(x2),
                        field.PixelsToTilesHeight(y2), flag);
            }
            return null;
        }

        public void Limit(int[] list)
        {
            if (field != null)
            {
                this.limit = list;
                this.dirty = true;
            }
        }

        public void SetBlockSize(int tileWidth, int tileHeight)
        {
            this.tileWidth = tileWidth;
            this.tileHeight = tileHeight;
        }

        public int GetWidth()
        {
            return width * tileWidth;
        }

        public int GetHeight()
        {
            return height * tileHeight;
        }

        public PackTile GetTile(int x, int y)
        {
            return tiles[x][y];
        }

        public void SetTile(int x, int y, PackTile type)
        {
            tiles[x][y] = type;
        }

        public bool CollidesWith(PackSprite sprite)
        {
            return !CanMove(sprite, sprite.GetX(), sprite.GetY());
        }

        public int Count(PackTile element)
        {
            int result = 0;
            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    if (element == tiles[x][y])
                    {
                        result++;
                    }
                }
            }
            return result;
        }

        public bool CanMove(PackSprite sprite, float x1, float y1)
        {
            float w = sprite.GetWidth();
            float h = sprite.GetHeight();
            float sx = x1 - w;
            float sy = y1 - h;
            for (int x = MathUtils.Max(MathUtils.Floor(sx / tileWidth), 0); x <= MathUtils
                    .Min(MathUtils.Floor((sx + w - 1) / tileWidth), width - 1); x++)
            {
                for (int y = MathUtils.Max(MathUtils.Floor(sy / tileHeight), 0); y <= MathUtils
                        .Min(MathUtils.Floor((sy + h - 1) / tileHeight), height - 1); y++)
                {
                    if (tiles[x][y].IsSolid())
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        public void Touch(PackSprite sprite)
        {
            float w = sprite.GetWidth();
            float h = sprite.GetHeight();
            float sx = sprite.GetX() - w;
            float sy = sprite.GetY() - h;
            for (int x = MathUtils.Max(MathUtils.Floor(sx / tileWidth), 0); x <= MathUtils
                    .Min(MathUtils.Floor((sx + w - 1) / tileWidth), width - 1); x++)
            {
                for (int y = MathUtils.Max(MathUtils.Floor(sy / tileHeight), 0); y <= MathUtils
                        .Min(MathUtils.Floor((sy + h - 1) / tileHeight), height - 1); y++)
                {
                    tiles[x][y] = tiles[x][y].Touch(sprite, x * tileWidth, y
                            * tileHeight);
                }
            }
        }

        public bool DrawSub(LTexturePack pack)
        {
            return Draw(pack, minX, minY, maxX, maxY);
        }

        public bool Draw(LTexturePack pack)
        {
            return Draw(pack, 0, 0);
        }

        public bool Draw(LTexturePack pack, int minX, int minY)
        {
            return Draw(pack, minX, minY, limitWidth - minX, limitHeight - minY);
        }

        public bool Draw(LTexturePack pack, int minX, int minY, int maxX,
                int maxY)
        {

            if (dirty)
            {
                Update();
            }

            LTexturePack currentPack = null;
            long elapsedTime = 0;
            int size = 0;
            if (screen != null)
            {
                if (bindPackName == null
                        || pack.GetName().Equals(bindPackName, StringComparison.InvariantCultureIgnoreCase))
                {
                    currentPack = pack;
                }
                else
                {
                    if (screen is Screen)
                    {
                        ScriptScreen script = (ScriptScreen)screen;
                        currentPack = script.GetPack(bindPackName);
                        if (currentPack == null)
                        {
                            currentPack = pack;
                        }
                        size = script.GetPackSprites().Size();
                    }
                }
                elapsedTime = screen.elapsedTime;
            }
            else
            {
                currentPack = pack;
            }

            bool update = (size != 0 && currentPack == pack);


            currentPack.GLBegin();
            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    PackTile tile = tiles[x][y];
                    if (tile != null)
                    {
                        sx = minX + x;
                        sy = minY + y;
                        if (sx + tile.Width() < minX || sx > maxX
                                || sy + tile.Height() < minY || sy > maxY)
                        {
                            continue;
                        }
                        if (lightingOn)
                        {
                            SetLightColor(sx, sy);
                        }
                        tile.Update(elapsedTime);
                        tile.Draw(currentPack,
                                posX + view.WorldToRealX(sx * tileWidth), posY
                                        + view.WorldToRealY(sy * tileHeight), LColor.white);
                    }
                }
            }
            currentPack.GLEnd();




            return update;

        }

        public string GetName()
        {
            return name;
        }

        public PackTileFactory GetTileFactory()
        {
            return tileFactory;
        }

        public void SetTileFactory(PackTileFactory tileFactory)
        {
            this.tileFactory = tileFactory;
            this.dirty = true;
        }

        public int GetTileWidth()
        {
            return tileWidth;
        }

        public int GetTileHeight()
        {
            return tileHeight;
        }

        public Field2D GetField2D()
        {
            return field;
        }

        public int[] GetLimit()
        {
            return limit;
        }

        public PackView GetView()
        {
            return view;
        }

        public void SetView(PackView view)
        {
            this.view = view;
        }

        public int GetPosX()
        {
            return posX;
        }

        public void SetPosX(int posX)
        {
            this.posX = posX;
        }

        public int GetPosY()
        {
            return posY;
        }

        public void SetPosY(int posY)
        {
            this.posY = posY;
        }

        public void SetPos(int x, int y)
        {
            this.posX = x;
            this.posY = y;
        }

        public Screen GetScreen()
        {
            return screen;
        }

        public void Dispose()
        {
            if (tiles != null)
            {
                tiles = null;
            }
            if (field != null)
            {
                field = null;
            }
            if (tileFactory != null)
            {
                if (tileFactory is SimplePackTileFactory)
                {
                    tileFactory = null;
                }
            }
            dirty = false;
            init = false;
        }

    }

}
